/**
  * @(#)DynamicDatabaseManager.java
  *
  * A database manager implementation which will dynamically select the correct
  * database implementation for each database. An application instance may 
  * have multiple open databases, each being used for different purposes. For
  * instance, the user may have a database connection to a university database
  * for the purpose of downloading the courses from the school's course system
  * as well as another database for local storage.
  *
  * @author Course Scheduler Team
  * 
  * @license GNU General Public License version 3 (GPLv3)
  *
  * This file is part of Course Scheduler, an open source, cross platform
  * course scheduling tool, configurable for most universities.
  *
  * Copyright (C) 2010-2012 Course Scheduler Team
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * 
  */
package com.pollicitus.scheduler.database;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.pollicitus.scheduler.core.Application;
import com.pollicitus.scheduler.core.Manager;
import com.pollicitus.scheduler.core.Settings;
import com.pollicitus.scheduler.database.DatabaseClient;
import com.pollicitus.scheduler.database.DatabaseManager;

/**
 * A database manager implementation which will dynamically select the correct
 * database implementation for each database. An application instance may 
 * have multiple open databases, each being used for different purposes. For
 * instance, the user may have a database connection to a university database
 * for the purpose of downloading the courses from the school's course system
 * as well as another database for local storage.
 * 
 * @author Course Scheduler Team
 *
 */
public class DynamicDatabaseManager extends DatabaseManager{
	
	//Instance variables
	protected Set<String> loadedNamespaces;
	protected Map<String, DatabaseManager> activeManagers;
	protected Map<String, DatabaseManager> openDatabases;
	
	/* (non-Javadoc)
	 * @see com.scheduler.core.Manager#initialize(com.scheduler.core.Settings)
	 */
	@Override
	public void initialize(Settings settings) throws InstantiationException, IllegalAccessException, ClassNotFoundException, FileNotFoundException, IOException {
		loadedNamespaces = new HashSet<String>();
		activeManagers = new HashMap<String, DatabaseManager>();
		openDatabases = new HashMap<String, DatabaseManager>();
	}

	/* (non-Javadoc)
	 * @see com.scheduler.database.DatabaseManager#getDatabaseClient(java.lang.String, java.lang.String)
	 */
	@Override
	protected DatabaseClient getDatabaseClient(String databaseName, String databaseContext) {
		DatabaseManager manager = null;
		
		//instantiate the manager if necessary
		try{
			manager = instantiateDatabaseManager(databaseName);
		}catch(Exception e){
			//TODO how to handle instantiation exceptions in the above manager invocation?
		}
		
		//retrieve a database client from the db manager and return it
		DatabaseClient client = manager.getClient(databaseName, databaseContext);
		log.info("Database client {} created for {}", client, databaseName);
		return client;
	}

	/* (non-Javadoc)
	 * @see com.scheduler.database.DatabaseManager#finalize(com.scheduler.core.Settings)
	 */
	@Override
	public void finalize(Settings settings) {
		//TODO call the finalize method for each active database manager
		//		do we need to define a shutdown order for the databases to
		//		insure consistency or safety?
	}

	/* (non-Javadoc)
	 * @see com.scheduler.database.DatabaseManager#getDefaultContext(java.lang.String)
	 */
	@Override
	protected String getDefaultContext(String databaseName) {
		DatabaseManager manager = null;
		
		//instantiate the database manager if necessary
		try{
			manager = instantiateDatabaseManager(databaseName);
		}catch(Exception e){
			//TODO how to handle instantiation exceptions
		}
		
		//return the default context
		return manager.getDefaultContext(databaseName);
	}
	
	/**
	 * Instantiate the database manager for the specified database (if an instance
	 * does not already exist). DatabaseManager implementations must be able to
	 * support multiple open databases at once.
	 * 
	 * @param databaseName the name of the database for which to instantiate the manager
	 * @return the manager instasnce for this database
	 * @throws ClassNotFoundException if database manager class not found
	 * @throws IllegalAccessException if database manager class cannot be accessed
	 * @throws InstantiationException if database manager class cannot be instantiated
	 * @throws IOException if the configuration file cannot be loaded from disk
	 * @throws FileNotFoundException if the configuration file is not found
	 */
	protected DatabaseManager instantiateDatabaseManager(String databaseName) throws FileNotFoundException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException{
		log.trace("Checking for active database manager for database {}", databaseName);
		DatabaseManager manager = openDatabases.get(databaseName);
		
		if(manager == null){
			log.trace("No active database manager for {}", databaseName);
			
			//lookup the correct db manager for the database name
			String managerClass = settings.getProperty(databaseName + SEPARATOR + MANAGER_CLASS);

			//check if a database manager instance already exists for this database type
			log.trace("Checking for active database manager {}", managerClass);
			manager = activeManagers.get(managerClass);
			
			if(manager == null){
				log.trace("No active instance of {}", managerClass);
				
				//lookup the manager namespace
				String settingsNamespace = settings.getProperty(managerClass + SEPARATOR + MANAGER_NAMESPACE);
				//lookup the manager config file
				String confFile = settings.getProperty(managerClass + SEPARATOR + CONFIG_FILE);
				
				//instantiate the database manager and store the database manager against the database name
				manager = (DatabaseManager)Manager.getInstance(Application.settings, managerClass, confFile, settingsNamespace);
				activeManagers.put(managerClass, manager);
			}
			openDatabases.put(databaseName, manager);
		}
		return manager;
	}
}
